/*
 * DataParser.cpp
 *
 *  Created on: 11.12.2011
 *      Author: serafim
 */
#include <iostream>
#include <sstream>
#include <list>
#include <map>
#include <vector>
#include <cmath>

#include <Poco/Net/HTTPClientSession.h>
#include <Poco/Net/HTTPRequest.h>
#include <Poco/Net/HTTPResponse.h>
#include <Poco/StreamCopier.h>
#include <Poco/URI.h>
#include <Poco/RegularExpression.h>
#include <Poco/NumberFormatter.h>

#include <htmlcxx/html/ParserDom.h>
#include <htmlcxx/html/Node.h>
#include <htmlcxx/html/tree.h>
#include <htmlcxx/html/Extensions.h>
#include <htmlcxx/html/utils.h>
using namespace htmlcxx;

using std::string;
using std::cin;
using std::cout;
using std::endl;
using std::vector;
using namespace Poco::Net;

#include "DataParser.h"
#include "Interface.h"

DataParser::DataParser() {
}

DataParser::~DataParser() {
}


DataObject* DataParser::parseDataOnPage(string url){
	string source;
	source = getPageSource(url);
	DataObject* obj = new DataObject;

	obj->setDownloads( getImageDownloads(source) );
	obj->setViews( getImageViews(source) );
	obj->setId( getImageId(url) );

	return obj;
}

//Вернуть список ссылок на страницы по ключевому слову
vector<string*> DataParser::getUrlsByKeyword(string keyword, int maxNum){
	//cout << "Ключевое слово: " << keyword << endl;
	Interface::Instance()->setStatusMessage("Ключевое слово: " + keyword);
	//обработка строки
	for(unsigned int i=0; i<keyword.length(); ++i)
		if(keyword[i]==' '){
			keyword.insert(i, "%20");
			i+=3;
			keyword.erase(i, 1);
		}

	string url = DREAMSTIME + string("/search.php?srh_field=") + keyword;
	string source = getPageSource(url);

	//получение списка ссылок на результаты поиска
	int num = getNumberOfPages(source);
	//cout << "Найдено " << num << " страниц" << endl;
	Interface::Instance()->setStatusMessage("Найдено " + Poco::NumberFormatter::format(num) + " страниц");
	//cout << "Формирую ссылки на изображения (" << maxNum << " ссылок)" << endl;
	Interface::Instance()->setStatusMessage("Формирую ссылки на изображения (" + Poco::NumberFormatter::format(maxNum) + " ссылок)");
	//cout << "...обрабатываются страницы..." << endl;
	Interface::Instance()->setStatusMessage("...обрабатываются страницы...");

	vector<string*> links;
	for(int i=1; i<num+1; ++i){
			//получить вектор ссылок с текущей страницы
		//cout << url+"&pg="+ Poco::NumberFormatter::format(i) << endl;
		vector<string*> temp = getImageURLsFromPage(url+"&pg="+ Poco::NumberFormatter::format(i));
			//добавить их в вектор links
		for(vector<string*>::iterator j=temp.begin(); j<temp.end(); ++j){
			links.push_back(*j);
			if(links.size() >= (unsigned int)maxNum){
				i = num+1;
				break;
			}
		}
	}
	return links;
}



//вернуть ссылки на все фотографии одной страницы
vector<string*>& DataParser::getImageURLsFromPage(string url){
	vector<string> urls = getAllLinks(url);
	vector<string> images;

	for(vector<string>::iterator it=urls.begin(); it!=urls.end(); ++it){
		if(isImageUrl(*it))
			images.push_back(*it);
	}

	vector<string*>* result = new vector<string*>;
	for(vector<string>::iterator it=images.begin(); it!=images.end(); it+=2){
		result->push_back(new string(DREAMSTIME + *it));
	}

	return *result;
}


//вернуть вектор ссылок на все страницы поиска
vector<string> DataParser::getPageURLs(string source){
	htmlcxx::HTML::ParserDom parser;
	tree<htmlcxx::HTML::Node> dom = parser.parseTree(source);

	vector<string> pageUrls;
	tree<HTML::Node>::iterator it;

	for(it=dom.begin(); it!=dom.end(); ++it){
		if(it->tagName() =="a"){
			it->parseAttributes();
			string href = it->attribute("href").second;
			if(href.find("&pg=",0)!=string::npos){
				pageUrls.push_back(href);
			}
		}
	}
	return pageUrls;
}


//вернуть количество страниц
int DataParser::getNumberOfPages(string source){
	vector<string> pageUrls = getPageURLs(source);

	//обработка pageUrls
	int max = 0;
	for(vector<string>::iterator pages=pageUrls.begin(); pages!=pageUrls.end(); ++pages){
		int pos = (*pages).find("&pg=", 0);
		pos+=4;
		int current = StringToInt( (*pages).substr(pos, (*pages).length()) );

		if(pages==pageUrls.begin())
			max = current;
		if(max < current)
			max = current;
	}

	return max;

}

bool DataParser::isImageUrl(string url){
	Poco::RegularExpression exp("^/(([A-Za-z0-9]+|[0-9]{1})-)+(-)?image[0-9]*.");
	return exp.match(url);
}

//вернуть все ссылки на странице
vector<string> DataParser::getAllLinks(string url){
	string source = getPageSource(url);
	htmlcxx::HTML::ParserDom parser;
	tree<htmlcxx::HTML::Node> dom = parser.parseTree(source);

	vector<string> pageUrls;
	tree<HTML::Node>::iterator it;

	for(it=dom.begin(); it!=dom.end(); ++it){
		if(it->tagName() =="a"){
			it->parseAttributes();
			string href = it->attribute("href").second;
			if(href!="")
				pageUrls.push_back(href);
		}
	}
	return pageUrls;
}


//вернуть исходник страницы
string DataParser::getPageSource(string url){
	if(url=="")
		return "";
	Poco::URI uri(url);
	HTTPClientSession session(uri.getHost(), uri.getPort());
	HTTPRequest req(HTTPRequest::HTTP_GET, url);
	session.sendRequest(req);
	HTTPResponse res;
	std::istream& rs = session.receiveResponse(res);
	string data;
	int bytes = Poco::StreamCopier::copyToString(rs, data);
	if(bytes)
		return data;
	else
		return "";
}

string DataParser::intToString(int v){
	std::ostringstream out;
	out << v;
	return out.str();
}

int DataParser::StringToInt(string s){
	//найти количество цифр в числе
	int i;
	for(i=0; (unsigned int)i<s.length(); ++i){
		if( !isdigit(s[i]) )
			break;
	}
	//формирование числа
	int result = 0;
	for(int j = 0; j<i; ++j)
		result += returnDigit(s[j]) * pow(10, i-j-1);

	return result;

}

int DataParser::returnDigit(char s){
	switch(s){
	case '1': return 1;
	case '2': return 2;
	case '3': return 3;
	case '4': return 4;
	case '5': return 5;
	case '6': return 6;
	case '7': return 7;
	case '8': return 8;
	case '9': return 9;
	case '0': return 0;
	default: return 0;
	}
}

bool DataParser::isInt(char a){
	switch(a){
	case '1':
	case '2':
	case '3':
	case '4':
	case '5':
	case '6':
	case '7':
	case '8':
	case '9':
	case '0':
		return true;
	default:
		return false;
	}

}
string DataParser::getTagFromSource(string source, string tag, string attribute, string value){
	if(source=="")
			return 0;

	htmlcxx::HTML::ParserDom parser;
	tree<htmlcxx::HTML::Node> dom = parser.parseTree(source);


	tree<HTML::Node>::iterator it;
	//tree<HTML::Node>::iterator_base it;

	for(it=dom.begin(); it!=dom.end(); ++it){
		if(it->tagName() == tag){
			it->parseAttributes();
			string cls = it->attribute(attribute).second;
			if(cls==value)
				return source.substr(it->offset(), it->length());
		}
	}
	return 0;
}


/*		Методы, предназначенные для парсинга данных со страницы изображения		*/


int DataParser::getImageDownloads(string source){
	if(source=="")
		return 0;
	string tag = getTagFromSource(source, "div", "class", "h2 item06");
	int key_pos = tag.find("Downloads:", 0)+11;
	return StringToInt( tag.substr(key_pos, tag.length()-key_pos) );
}


int DataParser::getImageViews(string source){
	if(source=="")
		return 0;
	string tag = getTagFromSource(source, "div", "class", "h2 item06");
	int key_pos = tag.find("Views:", 0)+7;
	return StringToInt( tag.substr(key_pos, tag.length()-key_pos) );
}

int DataParser::getImageId(string url){
	if(url=="")
		return 0;
	int last = url.length()-1;
	while(isInt(url[last]))
		--last;
	last++;
	if((unsigned int)last>url.length())
		return 0;
	else
		return StringToInt( url.substr( last, url.length()-last ) );
}
